import logging
import re
import webapp2
from google.appengine.api import mail
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from google.appengine.ext.webapp.util import run_wsgi_app

# From the Google AppEngine Documentation (http://code.google.com/appengine/docs/python/mail/sendingmail.html)
# The email address of the sender, the From address. The sender address must be one of the following types:
#  - The address of a registered administrator for the application. You can add administrators to an application using the Administration Console.
#  - The address of the user for the current request signed in with a Google Account. You can determine the current user's email address with the Users API.
#  - Any valid email receiving address for the app (such as xxx@APP-ID.appspotmail.com).
FROM_EMAIL = 'VALID_EMAIL'
PAGER_EMAILS = {\
	u'USER1' : 'EMAIL1',\
	u'USER2' : 'EMAIL2',\
	}

SMS_INTRO = 'SMS from '
VOICEMAIL_INTRO = 'New voicemail from '
MISSED_CALL_INTRO_SUBJECT = 'New missed call from '
MISSED_CALL_INTRO_MESSAGE = 'Missed call from '

def getToUsers(toList):
	#So the documentation says this is a list, but it appears to just be a string during the testing I did, so just handle both cases
	if isinstance(toList, basestring):
		return [toList]
	else:
		return toList

def isSMS(message):
	try:
		return message.subject.find(SMS_INTRO) == 0
	except:
		return False

def isGmailSMS(from_, text):
	return (from_.startswith('(406)') or from_ == 'Gmail SMS') and text.endswith('@gmail.com')

def isVoicemail(message):
	try:
		return message.subject.find(VOICEMAIL_INTRO) == 0
	except:
		return False

def isMissedCall(message):
	try:
		return message.subject.find(MISSED_CALL_INTRO_SUBJECT) == 0
	except:
		return False

def extractSMSFrom(subject):
	try:
		#Look for the intro
		iIntro = subject.index(SMS_INTRO)
		#And pull out the 'from'
		sms_from = subject[iIntro+len(SMS_INTRO):]
		#Then look if we can remove the phone number and any unnecessary whitespace
		return sms_from.split('[')[0].strip()
	except ValueError, e:
		logging.error("Error extracting 'from' for SMS: " + subject)
		return subject

def extractSMSText(content):
	#Split at the footer
	content_split = re.split('--\s+Sent using SMS-to-email.', content)
	#And if the split happened
	if len(content_split) > 1:
		#Then grab just the text before the footer
		content = content_split[0].strip()
	else:
		#Otherwise, just record that there's no footer
		logging.info("No footer for SMS: " + content)
	#Return the text without any unnecessary whitespace
	return content.strip()

def extractGmailSMSFromAndText(from_, text):
	try:
		#Look for the footer
		strFooter = '\n- '
		iFooter = text.rindex(strFooter)
		#Pull out the email the message is from
		from_ = text[iFooter+len(strFooter):]
		#And then get just the text portion
		text = text[0:iFooter].strip()
	except ValueError, e:
		logging.error("No footer for Gmail SMS: " + text)
	#Return the extracted values
	return (from_, text)

def extractFromWithAt(subject, intro):
	try:
		#Look for the intro
		iIntro = subject.index(intro)
		#Look for the time
		strTime = ' at '
		iTime = subject.index(strTime, iIntro)
		#And pull out the 'from' without any extra whitespace
		return (subject[iIntro+len(intro):iTime], subject[iTime+len(strTime):])
	except ValueError, e:
		logging.error("Error extracting 'from' for 'From with At': " + subject)
		return (subject, None)

def extractVoicemailFrom(subject):
	return extractFromWithAt(subject, VOICEMAIL_INTRO)[0]

def extractVoicemailTranscript(content):
	try:
		#Look for the transcript
		strTranscript = 'Transcript: '
		iTranscript = content.index(strTranscript)
		#Look for the footer
		iFooter = content.rindex('Play message: ')
		#And pull it out without any extra whitespace
		return content[iTranscript+len(strTranscript):iFooter].strip()
	except ValueError, e:
		logging.error("Error extracting transcript for voicemail: " + content)
		return content

def extractMissedCallFromAndTime(subject):
	return extractFromWithAt(subject, MISSED_CALL_INTRO_SUBJECT)

class GVPushHandler(InboundMailHandler):
	def receive(self, message):
		#Get who the message should be sent to
		toUsers = getToUsers(message.to)
		#Get the plain text version of the email
		plaintext_bodies = message.bodies('text/plain')
		#And grab the values from the generator
		for charset, body in plaintext_bodies:
			#Get the body of the message
			content = body.payload
			#Check if it's an SMS message
			if isSMS(message):
				#Get the who and the what from the SMS
				from_ = extractSMSFrom(message.subject)
				text_ = extractSMSText(body.payload)
				#If it's an SMS sent from Gmail
				if isGmailSMS(from_, text_):
					#Then pull out the email it was sent from
					(from_, text_) = extractGmailSMSFromAndText(from_, text_)
				from_ = "SMS from " + from_
			elif isVoicemail(message):
				#Get the who and the what from the voicemail
				from_ = "Voicemail from " + extractVoicemailFrom(message.subject)
				text_ = extractVoicemailTranscript(body.payload)
			elif isMissedCall(message):
				#Get the who and the what from the missed call
				(from_, time) = extractMissedCallFromAndTime(message.subject)
				from_ = "Missed Call from " + from_
				text_ = "Called at " + time
			else:
				#Get the who and the what from the email
				from_ = message.subject
				text_ = body.payload
				#Send the message back to were it came from so that the person can know it happened and this is probably the authorization code
				mail.EmailMessage(sender=FROM_EMAIL, to=message.to, subject=message.subject, body=body.payload).send()

			#Send the notification along
			logging.debug("[" + from_ + "] | [" + text_ + "]")
			for user in toUsers:
				try:
					to_ = PAGER_EMAILS[user]
					outMessage = mail.EmailMessage(sender=FROM_EMAIL, to=to_, subject=from_, body=text_)
					outMessage.send()
					logging.info("Sent to " + user + " (" + to_ + ")")
				except:
					logging.error("Message for unknown user: " + str(user))

#The global application object
app = webapp2.WSGIApplication([GVPushHandler.mapping()], debug=False)
